﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

using LanguageDetector.Data;

namespace LanguageDetector.Test
{
    [TestFixture]
    public class DB02_BiGramStatDB_CRUD : IDisposable
    {
        #region test vars + setup/teardowns
        Random rand = new Random();
        List<BiGramStat> stats = new List<BiGramStat>();
        LanguageStat tmpLang = null;

        //setup
        public DB02_BiGramStatDB_CRUD()
        {
            //take care of FK dependencies
            var existing = LanguageStatDB.Get(new LanguageCriteria());
            Assert.IsEmpty(existing.Errors);
            if (existing.IsNullOrEmpty())
            {
                tmpLang = new LanguageStat()
                {
                    ISO2 = "11223",
                    BiGramTotal = rand.Next(1, int.MaxValue / 100),
                    IncludeASCII = true,
                    Name = "Test Bigram Lang FK",
                };
                existing.Add(tmpLang);
                var errors = LanguageStatDB.Save(existing);
                Assert.IsEmpty(errors);
                Assert.Greater(tmpLang.ID, 0);
            }
            var langid = existing.First().ID;
            Assert.Greater(langid, 0);
            
            //attach actual test data here
            stats.Add(new BiGramStat()
            {
                BiGram = new BiGram() { A = '1', B = '1' },
                Count = 0,
                LanguageID = langid,
            });
            stats.Add(new BiGramStat()
            {
                BiGram = new BiGram() { A = '2', B = '3' },
                Count = 1,
                LanguageID = langid,
            });
            stats.Add(new BiGramStat()
            {
                BiGram = new BiGram() { A = '9', B = 'z' },
                Count = rand.Next(1, int.MaxValue / 100),
                LanguageID = langid,
            });
        }

        //tear down
        bool disposed = false;
        public void Dispose()
        {
            lock (this)
            {
                if (!disposed)
                {
                    disposed = true;
                    var errors = new List<Exception>();
                    var vac = false;
                    var ids = (from s in stats where s.ID > 0 select s.ID).ToList();
                    if (!ids.IsNullOrEmpty())
                    {
                        vac = true;
                        var sb = new StringBuilder("delete from bigram_stats where id in (");
                        ids.ForEach(id => sb.AppendFormat("{0},", id));
                        sb.Length--;
                        sb.Append(");");
                        var q = new Query() { Text = sb.ToString() };
                        errors.AddRange(q.ExecuteNoneReader());
                    }
                    if (tmpLang != null && tmpLang.ID > 0)
                    {
                        vac = true;
                        var q = new Query() { Text = string.Format("delete from language_stat where id = {0};", tmpLang.ID) };
                        errors.AddRange(q.ExecuteNoneReader());
                    }
                    if (vac)
                    {
                        var q = new Query() { Text = "Vacuum;" };
                        errors.AddRange(q.ExecuteNoneReader());
                    }
                    Assert.IsEmpty(errors);
                }
            }
        }
        ~DB02_BiGramStatDB_CRUD()
        {
            Dispose();
        }
        #endregion

        #region helper methods
        QueryResult<BiGramStat> GetByCriteria(BiGramCriteria f)
        {
            Assert.IsNotNull(f);
            var res = BiGramStatDB.Get(f);
            Assert.IsNotNull(res);
            Assert.IsNotEmpty(res);
            Assert.IsEmpty(res.Errors);
            return res;
        }

        int Compare(BiGramStat og, BiGramStat re)
        {
            Assert.IsNotNull(og);
            Assert.IsNotNull(re);
            Assert.AreEqual(og.ID, re.ID);
            Assert.AreEqual(og.BiGram, re.BiGram);
            Assert.AreEqual(og.Count, re.Count);
            Assert.AreEqual(og.LanguageID, re.LanguageID);
            return 1;
        }
        #endregion

        bool inserted = false;
        [Test]
        public void T10_INSERT()
        {
            stats.ForEach(s => Assert.AreEqual(0, s.ID));
            var errors = BiGramStatDB.Save(stats);
            Assert.IsEmpty(errors);
            stats.ForEach(s => Assert.Greater(s.ID, 0));

            inserted = true;
        }

        [Test]
        public void T20_LOAD_BY_ID()
        {
            if (!inserted)
                T10_INSERT();

            var f = new BiGramCriteria() { IDs = (from s in stats where s.ID>0 select s.ID).ToList() };
            var res = GetByCriteria(f);
            var tested = (from og in stats
                          join re in res on og.ID equals re.ID
                          select Compare(og, re)).Sum();
            Assert.AreEqual(stats.Count, tested);
        }

        [Test]
        public void T21_LOAD_MIN_COUNT()
        {
            if (!inserted)
                T10_INSERT();

            var counts = (from s in stats orderby s.Count select s.Count).ToArray();
            var f = new BiGramCriteria() { MinCount = counts[0] + 1 };
            var res = GetByCriteria(f);
            var tested = (from og in stats
                          join re in res on og.ID equals re.ID
                          select Compare(og, re)).Sum();
            res.ForEach(r => Assert.GreaterOrEqual(r.Count, f.MinCount));
            Assert.AreEqual(counts.Length - 1, tested);
        }

        bool updated = false;
        [Test]
        public void T30_UPDATE()
        {
            if (!inserted)
                T10_INSERT();

            for (var i = 0; i < stats.Count; i++)
            {
                var s = stats[i];
                s.BiGram = new BiGram(i.ToString()[0], (i + 2).ToString()[0]);
                s.Count+= i * 2;
            }
            var errors = BiGramStatDB.Save(stats);
            Assert.IsEmpty(errors);
            stats.ForEach(l => Assert.Greater(l.ID, 0));

            updated = true;
        }

        [Test]
        public void T40_RELOAD_UPDATED()
        {
            if (!updated)
                T30_UPDATE();

            var f = new BiGramCriteria() { IDs = (from s in stats where s.ID > 0 select s.ID).ToList() };
            var res = GetByCriteria(f);
            var tested = (from og in stats
                          join re in res on og.ID equals re.ID
                          select Compare(og, re)).Sum();
            Assert.AreEqual(stats.Count, tested);
        }

        bool deleted = false;
        [Test]
        public void T50_DELETE()
        {
            if (!inserted)
                T10_INSERT();

            var ids = (from s in stats where s.ID > 0 select s.ID).ToList();
            var errors = BiGramStatDB.Delete(ids);
            Assert.IsEmpty(errors);

            deleted = true;
        }

        [Test]
        public void T60_RELOAD_DELETED()
        {
            if (!deleted)
                T50_DELETE();

            var f = new BiGramCriteria() { IDs = (from s in stats where s.ID > 0 select s.ID).ToList() };
            var res = BiGramStatDB.Get(f);
            Assert.IsNotNull(res);
            Assert.IsEmpty(res);
            Assert.IsEmpty(res.Errors);
            stats.Clear();
        }
    }
}
